/**************************************************************************
File:        laser.cpp
Description:

Copyright:   (C) 2001-2012 by ZJ Laczik
Email:       Zsolt.Laczik@eng.ox.ac.uk
****************************************************************************
Change log:
   begin      Sun Mar 18 2001
   15/2/2012	QT4 update
***************************************************************************/

#include <QMessageBox>
#include <QTimer>

#include "micromachine.h"

#include "laser.h"

Laser::Laser(QWidget *parent) : QObject(parent) {

    delay_res=10;
    init_usec_delay();

    // close shutter and turn laser warning sign off
    WarningSignOff();

    mStopFlag = false;
    mNPulses = 1;
    mRepRate = 1;
    mTriggerDelay = 1;

    QTimer *t = new QTimer( this );
    connect( t, SIGNAL(timeout()), SLOT(CheckInterlock()) );
    t->setSingleShot( FALSE );
    t->start( 1000 );
}

Laser::~Laser() {
    WarningSignOff();
}

void Laser::Fire() {
    emit( Running(true) );
    Trigger();
    emit( StatusMsg(QString("Laser: Single shot fired"), true) );
    emit( Running(false) );
}

void Laser::Burst() {
    int i,delay;
    QString s;

    delay = 1000000/mRepRate;

    emit( StatusMsg(QString("Laser: Burst shots starting"), true) );
    emit( Running(true) );

    i=0;
    while (i<mNPulses) {
        if (mStopFlag) {
            emit( StatusMsg(QString("Laser: Stopping"), false) );
            break;
        }
        Trigger();
        usleep(delay);
        i++;
        qApp->processEvents();
    }
    s.sprintf("Laser: %4d of %4d burst shots fired",i,mNPulses);
    emit( StatusMsg(s, false) );
    emit( Running(false) );
    mStopFlag = false;
}

void Laser::ContFire() {
    int delay;
    QString s;

    delay = 1000000/mRepRate;

    emit( StatusMsg(QString("Laser: Start continuous firing"), true) );
    emit( Running(true) );
    while (!mStopFlag) {
        Trigger();
        usleep(delay);
        qApp->processEvents();
    }
    emit( StatusMsg(QString("Laser: Stopping"), false) );
    emit( Running(false) );
    mStopFlag = false;
}

void Laser::SetNPulses(int n) {
    if (n>0 && n!=mNPulses) {
        mNPulses = n;
        emit(NPulsesChanged(mNPulses));
    }
}

void Laser::SetRepRate(int r) {
    if (r>0 && r<=1000 && r!=mRepRate) {
        mRepRate = r;
        emit(RepRateChanged(mRepRate));
    }
}

void Laser::SetTriggerDelay(long int r) {
    if (r>=0 && r<=20000 && r!=mTriggerDelay) {
        mTriggerDelay = r;
        emit(TriggerDelayChanged(mTriggerDelay));
    }
}

void Laser::Trigger() {
    int cntl, cnth, ready;

    // check interlock
    while (!InterlockOK()) {
        // trouble - do we want to try again?
        int exit = QMessageBox::warning(0, "Laser interlock",
                                        "Laser interlock fail - Retry?",
                                        "Stop", "Retry");
        if (exit != 1) {
            // no - just stop
            WarningSignOff();
            mStopFlag = true;
            emit( sigStop() );
            return;
        }
    }

    if (!mShutterOpen) {
        int exit = QMessageBox::warning(0, "Shutter",
                                        "Shutter closed - Open shutter?",
                                        "No", "Yes");
        if (exit != 1) {
            // no - just stop
            WarningSignOff();
            mStopFlag = true;
            emit( sigStop() );
            return;
        }
        // else turn warning sign on
        WarningSignOn();
    }

    // For TI DMD DLP
    // --------------
    // sync. to DMD colour wheel signal rising edge
    // the timeout value of 15000 was determined by the commented lines below
    // for lith (P5/150) 'max' was ~7500
    // The timeout value corresponds to 60Hz
//  int timeout = 15000;
    //
    // For DisplayTech FE/LCD
    // wait for image valid to go high (same process as above, but extra delay not needed)
    //
    // With DisplayTech SLM running at 20 Hz
    // the timeout value was ~30000
    int timeout = 40000;
    cntl = cnth = 0;
    // wait for high (low on input) on S7
//	while ( (cntl++<timeout) && (inb(LPT_STAT)&0x40)) ;
//?/	do {ready=7; ioctl(ppdev,PP_GET_STATUS_BIT,&ready);} while ( (cntl++<timeout) && !ready) ;
    // wait for low (high on input)  on S7
//	while ( (cnth++<timeout) && !(inb(LPT_STAT)&0x40)) ;
//?/	do {ready=7; ioctl(ppdev,PP_GET_STATUS_BIT,&ready);} while ( (cnth++<timeout) && ready) ;
    // some extra delay comes here
    usec_delay(mTriggerDelay);
    // fire the laser
    if (InterlockOK())
      TriggerPulse();
//static int max=0;
//if (max<cntl) max=cntl;
//if (max<cnth) max=cnth;
//fprintf(stderr, "max  %5d\r",max);
}

void Laser::CheckInterlock() {
    if (!InterlockOK())
        WarningSignOff();
}

void Laser::Stop() {
    mStopFlag = true;
}

void Laser::TriggerPulse() {
    CheckInterlock();

    ((Micromachine*)parent())->mHal->mfSetLaserTriggerBit(true);
    usec_delay(1000);
    ((Micromachine*)parent())->mHal->mfSetLaserTriggerBit(false);
    usec_delay(1000);
};

void Laser::init_usec_delay()
{
    long int liTst,diff;
    struct timeval b,a;
    struct timezone d;

    delay_cal = 1;
    liTst = 1000000;

    gettimeofday(&b,&d);
    usec_delay(liTst);
    gettimeofday(&a,&d);

    diff = 1000000*(a.tv_sec-b.tv_sec) + (a.tv_usec-b.tv_usec);

    delay_cal = liTst/diff;
}

void Laser::usec_delay(long int usecs) {
    long int liLoop=(usecs*delay_cal)/delay_res;
    while (liLoop) liLoop--;
}

bool Laser::InterlockOK() {
    return ((Micromachine*)parent())->mHal->mfReadInterlockBit();
}

void Laser::WarningSignOff() {
    mShutterOpen = false;
    ((Micromachine*)parent())->mHal->mfSetWarningSignBit(false);
    return;
}

void Laser::WarningSignOn() {
    mShutterOpen = true;
    ((Micromachine*)parent())->mHal->mfSetWarningSignBit(true);
    return;
}
